package edu.gxmzu.task.utils;

import edu.gxmzu.task.config.security.MyUserDetails;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;


// 生成和解析JWT
@Component
public class JWTUtil {
//    @Value("${jwt.key}")
    private static final SecretKey key = new SecretKeySpec("admin123123admin123123admin123123".getBytes(), "HmacSHA256");; // 静态密钥, 够用就行
//    @Value("${jwt.expiration}")
    private static final Long expiration = 1000 * 60 * 60 * 8L; // 过期时长/ms

    /**
     * 初始化负载内数据
     * @param username 用户名
     * @return 负载集合
     */
    private static Map<String,Object> initClaims(String username){
        Map<String, Object> claims = new HashMap<>();
        // "iss" (Issuer): 代表 JWT 的签发者。在这个字段中填入一个字符串，表示该 JWT 是由谁签发的。例如，可以填入你的应用程序的名称或标识符。
        claims.put("iss","jx");
        // "sub" (Subject): 代表 JWT 的主题，即该 JWT 所面向的用户。可以是用户的唯一标识符或者其他相关信息。
        claims.put("sub", username);
        // "exp" (Expiration Time): 代表 JWT 的过期时间。通常以 UNIX 时间戳表示，表示在这个时间之后该 JWT 将会过期。建议设定一个未来的时间点以保证 JWT 的有效性，比如一个小时、一天、一个月后的时间。
        claims.put("exp", new Date(System.currentTimeMillis() + expiration));
        // "aud" (Audience): 代表 JWT 的接收者。这个字段可以填入该 JWT 预期的接收者，可以是单个用户、一组用户、或者某个服务。
        claims.put("aud","internal use");
        // "iat" (Issued At): 代表 JWT 的签发时间。同样使用 UNIX 时间戳表示。
        claims.put("iat",new Date());
        // "jti" (JWT ID): JWT 的唯一标识符。这个字段可以用来标识 JWT 的唯一性，避免重放攻击等问题。
        claims.put("jti", UUID.randomUUID().toString());
        // "nbf" (Not Before): 代表 JWT 的生效时间。在这个时间之前 JWT 不会生效，通常也是一个 UNIX 时间戳。我这里不填，没这个需求
        return claims;
    }

    /**
     * 根据负载生成JWT token
     * @param claims 负载
     * @return token
     */
    private static String generatorToken(Map<String,Object> claims){
        return Jwts.builder()
                .claims(claims)
                .signWith(key)
                .compact();
    }

    /**
     * 根据用户信息生成token
     *
     * @param username 用户名
     * @return token
     */
    public static String createToken(String userId, String username)
    {
        Map<String, Object> claims = initClaims(username);
        // 用户id
        claims.put("userId", userId);
        return generatorToken(claims);
    }
    /**
     * 从Token中获取负载中的Claims
     * @param token token
     * @return 负载
     */
    private static Claims getPayloadFromToken(String token)
    {
        return Jwts.parser()
                .verifyWith(key)
                .build()
                .parseSignedClaims(token)
                .getPayload();
    }
    /**
     * 从Token中获取用户id
     * @param token token
     * @return 用户id
     */
    public static String getUserIdFromToken(String token){
        String userId;
        try
        {
            userId = (String) getPayloadFromToken(token).get("userId");
        }catch (Exception e){
            userId = null;
        }
        return userId;
    }
    /**
     * 从Token中获取用户名
     * @param token token
     * @return 用户名
     */
    public static String getUserNameFromToken(String token){
        String username;
        try
        {
            username = getPayloadFromToken(token).getSubject();
        }catch (Exception e){
            username = null;
        }
        return username;
    }
    /**
     * 从token中获取预设的过期时间
     * @param token token
     * @return 预设的过期时间
     */
    private static Date getExpiredDateFromToken(String token)
    {
        return getPayloadFromToken(token).getExpiration();
    }
    /**
     * 判断token是否有过期
     * @param token 需要被验证的token
     * @return true/false
     */
    private static boolean isTokenExpired(String token)
    {
        //判断预设时间是否在当前时间之前，如果在当前时间之前，就表示过期了，会返回true
        return getExpiredDateFromToken(token).before(new Date());
    }
    /**
     * 验证token是否有效
     * @param token 需要被验证的token
     * @param myUserDetails true/false
     * @return
     */
    public static boolean validateToken(String token, MyUserDetails myUserDetails){
        String username = getUserNameFromToken(token);
        return (username.equals(myUserDetails.getUsername()) || username.equals(myUserDetails.getAccount().getEmail())) && !isTokenExpired(token);
    }
    /**
     * 判断token是否可以被刷新
     * @param token 需要被验证的token
     * @return true/false
     */
    public static boolean canRefresh(String token){
        return !isTokenExpired(token);
    }
    /**
     * 刷新token
     * @param token 需要被刷新的token
     * @return 刷新后的token
     */
    public static String refreshToken(String token){
        Claims claims = getPayloadFromToken(token);
        Map<String, Object> initClaims = initClaims(claims.getSubject());
        initClaims.put("iat",new Date());
        return generatorToken(initClaims);
    }

}
